package net.chemistry;

import static net.util.CharSequenceUtil.SUPER_1;
import static net.util.CharSequenceUtil.SUPER_2;
import static net.util.CharSequenceUtil.SUPER_3;
import static net.util.CharSequenceUtil.SUPER_4;
import static net.util.CharSequenceUtil.SUPER_5;
import static net.util.CharSequenceUtil.SUPER_6;

import java.io.Serializable;

import net.ChemistryException;
import net.util.ChemistryUtil;
import net.util.GraphicsUtil;
import net.util.MathUtil;

public class ElectronShell implements Serializable
{
	private static final long serialVersionUID = 3521028423098327464L;
	
	private int level, eInLevel, eShared;
	
	/**
	 * Constructs a new electron cloud at the given level and a full compliment of electrons (the shell will be full).
	 * 
	 * @param level The energy level this shell represents.
	 * @throws IllegalArgumentException If the level is zero or negative.
	 * */
	public ElectronShell(int level)
	{
		this(level, 2 * (level * level));
	}

	/**
	 * Constructs a new electron cloud at the given level and <code>e</code> electrons.
	 * 
	 * @param level The energy level this shell represents.
	 * @param e The initial number of electrons possessed by the shell.
	 * @throws IllegalArgumentException If the number of electrons provided is negative or too large, or the level is zero or
	 * negative.
	 * */
	public ElectronShell(int level, int e)
	{
		if (e < 0 || level < 0 || e > 2 * level * level)
		{
			throw new IllegalArgumentException("Too many electrons in level " + level + ": " + e);
		}
		this.level = level;
		this.eInLevel = e;
		this.eShared = 0;
	}	
	
	public String toString()
	{
		String str = String.valueOf(level) + "S" + (eInLevel == 1 ? SUPER_1 : SUPER_2);
		
		if (eInLevel > 2)
		{
			int n = eInLevel - 2;
			str += " " + level + "P" + (n == 1 ? SUPER_1 : 
										n == 2 ? SUPER_2 :
										n == 3 ? SUPER_3 :
										n == 4 ? SUPER_4 :
										n == 5 ? SUPER_5 :
										n == 6 ? SUPER_6 : '?');
		}
		
		return str;
	}
	
	public int getLevel()
	{
		return level;
	}

	public int geteInLevel()
	{
		return eInLevel + eShared;
	}
	
	/**
	 * Returns <code>true</code> if and only if the shell is completely full and cannot accept additional electrons, 
	 * <code>false<code> otherwise.
	 * 
	 * @return Whether or not the shell is full.
	 * @see isNoble
	 * */
	public boolean isFull()
	{
		return eInLevel + eShared == ChemistryUtil.nobleConfig(level);
	}
	
	/**
	 * Returns <code>true</code> if and only if the shell is completely full <b>taking only electrons fully possessed by this
	 * shell.</b> Electrons shared with other atoms do not count towards this function.
	 * 
	 *  @return Wehther or not the shell is completekly full, considering only fully possessed electrons.
	 *  @see isFull
	 * */
	public boolean isNoble()
	{
		return eInLevel == ChemistryUtil.nobleConfig(level);
	}

	public void oxidize()
	{
		
	}
	
	public void reduce() throws ChemistryException
	{
		if (eInLevel == 1)
		{
			throw new ChemistryException("Reducing yields empty shell. The shell should be removed instead of reduced.");
		}
		
		eInLevel --;
	}

	public void share() throws ChemistryException
	{
		if (isFull())
		{
			throw new ChemistryException("Cannot share a full shell.");
		}
		else if (level > 1 && eInLevel < 4)
		{
			throw new ChemistryException("Hund's rule forbids sharing electrons until all orbitals have one electron.");
		}
		
		eShared ++;
	}
	
	public void antiShare() throws ChemistryException
	{
		if (eShared == 0)
		{
			throw new ChemistryException("No shared electrons to lose. Use reduce instead.");
		}
		
		eShared --;
	}
	
	public void sever()
	{
		
	}
	
	public void render(int x, int y, float angle)
	{
		int numPairs;
		
		if (level == 1)
		{
			numPairs = eInLevel == 2 ? 1 : 0;
		}
		else
		{
			numPairs = eInLevel - 4;
		}
		
		float r = angle;
		
		int x1, y1;
		
		for(int n = 0; n < numPairs; n++)
		{
			r = (r + 90) % 360;
			
			x1 = x + (int)Math.round(MathUtil.lx(level, r));
			y1 = y + (int)Math.round(MathUtil.ly(level, r));
			
			GraphicsUtil.renderPolygon(x1, y1, 4, 5);
		}
	}
}
